﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using PlanificadorDeEventos.Repositorios;
using PlanificadorDeEventos.Interfaces;

namespace PlanificadorDeEventos.Repositorios
{
    /// <summary>
    /// Repositorio genérico para cualquier tipo de entidad
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public class RepositorioGenerico<TEntity> : IRepositorio<TEntity>
        where TEntity : class
    {
        #region Attributes

        internal DbContext context;
        internal DbSet<TEntity> dbSet;

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor de la clase
        /// </summary>
        /// <param name="context">El contexto que administra el repositorio.</param>
        public RepositorioGenerico(DbContext context)
        {
            this.context = context;
            this.dbSet = context.Set<TEntity>();
        }

        #endregion

        #region  IRepository<TEntity> Members

        /// <summary>
        /// Obtiene todas las entidades del repositorio.
        /// </summary>
        /// <returns></returns>
        public virtual IEnumerable<TEntity> GetAll()
        {
            return this.Get();
        }

        /// <summary>
        /// Obtiene un conjunto de entidades filtrando por sus propiedades.
        /// </summary>
        /// <param name="filter">El filtro.</param>
        /// <param name="orderBy">El orden del resultado.</param>
        /// <param name="includeProperties">Las propiedades a incluir de la entidad.</param>
        /// <returns></returns>
        public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, 
                                            Func<IQueryable<TEntity>,IOrderedQueryable<TEntity>> orderBy = null, 
                                                string includeProperties = "")
        {
           
            IQueryable<TEntity> query = dbSet;

            if (filter != null)
                query = query.Where(filter);

            foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                query = query.Include(includeProperty);

            if (orderBy != null)
                return orderBy(query).ToList();
            else
                return query.ToList();
        }

        /// <summary>
        /// Obtiene una entidad por su id
        /// </summary>
        /// <param name="id">El identificador.</param>
        /// <returns></returns>
        public virtual TEntity GetByID(object id)
        {
            return dbSet.Find(id);
        }

        /// <summary>
        /// Inserta una entidad específica.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public virtual void Insert(TEntity entity)
        {
            dbSet.Add(entity);
            context.SaveChanges();
        }

        /// <summary>
        /// Elimina una entidad por su id.
        /// </summary>
        /// <param name="id">El identificador.</param>
        public virtual void Delete(object id)
        {
            TEntity entityToDelete = dbSet.Find(id);
            Delete(entityToDelete);
        }

        /// <summary>
        /// Elimina una lista de entidades.
        /// </summary>
        /// <param name="List">La lista de entidades a eliminar</param>
        public virtual void DeleteAll(List<TEntity> List)
        {
            foreach (TEntity e in List)
            {
                this.Delete(e);
            }
        }


        /// <summary>
        /// Elimina una entidad específica.
        /// </summary>
        /// <param name="entityToDelete">La entidad a eliminar</param>
        public virtual void Delete(TEntity entityToDelete)
        {
            if (context.Entry(entityToDelete).State == EntityState.Detached)
                dbSet.Attach(entityToDelete);
            dbSet.Remove(entityToDelete);
            context.SaveChanges();
        }

        /// <summary>
        /// Actualiza una entidad específica
        /// </summary>
        /// <param name="entityToUpdate">La entidad a actualizar.</param>
        public virtual void Update(TEntity entityToUpdate)
        {
            context.Entry(entityToUpdate).State = EntityState.Modified;
            context.SaveChanges();
        }
        #endregion
    }
}
